<template>
	<e-transition mode="fade" :show="show" :disabledTouchmove="false" :duration="fade ? duration : 0">
		<prevent :stop="stop">
			<view class="app-components-load-image" :id="elId" :style="[wrapStyle, backgroundStyle]" @tap="onClick" >
				<template v-if="loading&&!isError">
					<image 
					<!-- #ifndef APP-NVUE -->
						v-show="loadEnd"
					<!-- #endif -->
					 :src="imgSrc" :mode="mode" @error="onErrorHandler" @load="onLoadHandler"
						:show-menu-by-longpress="showMenuByLongpress" :lazy-load="lazyLoad" class="app-components-load-image__image" :class="[['widthFix','heightFix'].includes(mode) ? '': 'e-abso' ]"
						:style="baseStyle"></image>
					
					
				</template>
				
				<view v-else-if="showError" class="app-components-load-image__error" :style="baseStyle">
					<slot name="error">
						<view class="app-components-load-image__error__box">
							<image src="./error.png" class="app-components-load-image__error__img"></image>
						</view>
					</slot>
				</view>
				<view v-if="showLoading && !loadEnd" class="app-components-load-image__loading" :style="loadingStyle">
					<slot name="loading">
						<e-loading-icon></e-loading-icon>
					</slot>
				</view>
				
			</view>
		</prevent>
	</e-transition>
</template>

<script lang="ts">
	/**
	 * 
	 * @property {String}						src					图片地址
	 * @property {String}						mode				裁剪模式，见官网说明 （默认 'aspectFill' ）
	 * @value aspectFit
	 * @value aspectFill
	 * @value widthFix
	 * @value heightFix
	 * @value top
	 * @value bottom
	 * @value right
	 * @value left
	 * @value center
	 * @value top left
	 * @value top right
	 * @value bottom left
	 * @value bottom right
	 * @property {Boolean}						lazyLoad			图片懒加载,默认关闭，微信小程序、百度小程序、字节跳动小程序、飞书小程序使用image自带lazyLoad属性，其余通过scrollTop和viewHeight属性实现懒加载功能（nvue不支持懒加载）
	 * @property {Number}						scrollTop			可视区域滚动值，通过监听该值的变化来判断是否加载图片，仅在开启lazyLoad为true时生效
	 * @property {Number}						viewHeight			可视区域高度
	 * @property {Boolean}						mobile				是否开启动态地址，即src属性会与global.host进行拼接，如果传入的图片是本地地址，可关闭该选项
	 * @property {String|Array<String>}			size				图片大小，只传一位代表宽高一致
	 * @property {String}						shape				图片形状，circle-圆形，square-方形
	 * @value circle
	 * @value square
	 * @property {String}						radius				圆角值，仅shape为square时生效
	 * @property {Boolean}						fade				是否开启淡入效果
	 * @property {Number}						duration			淡入效果过渡时间
	 * @property {String}						bgColor 			背景颜色，用于深色页面加载图片时，为了和背景色融合  (默认 '#f3f4f6' )
	 * @property {Boolean}						showLoading 		是否显示加载中的图标或者自定义的slot （默认 true ）
	 * @property {Boolean}						showError 			是否显示加载错误的图标或者自定义的slot （默认 true ）
	 * @property {Boolean}						showMenuByLongpress	是否开启长按图片显示识别小程序码菜单，仅微信小程序有效 （默认 true ）
	 * @property {Boolean}						stop				是否阻止事件冒泡
	 * @property {Boolean}						preview				点击图片是否预览
	 * @property {Array<String>}				previewUrls			预览图片列表，不传则为当前图片
	 * @property {Number}						previewCurrent		预览图片索引，不传则为第一张
	 * 
	 * @event {Function} click			点击事件
	 * @example <load-image :size="['100rpx']" src="xxx"></load-image>
	 */
	import { defineComponent } from 'vue';
	export default defineComponent({
		name: 'load-image',
		// #ifdef MP-WEIXIN
		// 将自定义节点设置成虚拟的，更加接近Vue组件的表现，能更好的使用e-flex属性
		options: {
			virtualHost: true
		}
		// #endif
	})
</script>
<script lang="ts" setup>
	import { ref, computed, watch,getCurrentInstance } from 'vue'
	import {
		fullLink,
		isString,
		nanoid,
		getRect
	} from "@/common/utils";
	import {
		useGlobal
	} from '@/common/stores'
	import {
		storeToRefs
	} from 'pinia'
	import prevent from './prevent.vue'

	type modeTypes = 'scaleToFill' | 'aspectFit' | 'aspectFill' | 'widthFix' | 'heightFix' | 'top' | 'bottom' | 'center' | 'left' | 'right' | 'top left' | 'top right' | 'bottom left' | 'bottom right'

	interface LoadImageProps {
		src?: string,
		mode ?: modeTypes,
		lazyLoad ?: boolean,
		scrollTop?:number,
		viewHeight?:number
		mobile ?: boolean,
		size ?:string| [string] | [string,string],
		shape ?: 'square' | 'circle',
		radius ?: string
		fade ?: boolean,
		duration ?: string | number,
		bgColor ?: string,
		showError ?: boolean,
		showLoading ?: boolean,
		showMenuByLongpress ?: boolean
		stop?:boolean
		preview?:boolean
		previewUrls?:string[]
		previewCurrent?:number
	}


	const props = withDefaults(defineProps<LoadImageProps>(), {
		mode: 'scaleToFill',
		lazyLoad: false,
		mobile: true,
		size: () => (['320px', '240px']),
		shape: 'square',
		radius: '0px',
		fade: true,
		duration: 300,
		bgColor: '#f3f4f6',
		showError: true,
		showLoading: true,
	})

	const emit = defineEmits(['click', 'error', 'load'])
	
	const ctx = getCurrentInstance()

	const {
			global
		} = storeToRefs(useGlobal())

	const elId = nanoid(21,true)

	// 图片是否加载错误，如果是，则显示错误占位图
	const isError = ref(false)
	// 是否加载图片，未开启lazyLoad时默认为true
	const loading = ref(false)
	
	// 图片加载完成，可以显示图片
	const loadEnd = ref(false)


	// 图片加载完成时，去掉背景颜色，因为如果是png图片，就会显示灰色的背景
	const backgroundStyle = ref({})
	// 用于fade模式的控制组件显示与否
	const show = ref(true)

	const imgSrc = computed(() => {
		if (props.mobile&&props.src) {
			const fullSrc = fullLink(global.value?.host,props.src)
			return fullSrc
		}
		return props.src
	})

	const baseStyle = computed(() => {
		let style : Record<string, string | number> = {}
		const size = isString(props.size) ? [props.size]: props.size
		if(props.mode !=='heightFix'){
			style.width = size[0]
		}
		
		if(props.mode !=='widthFix'){
			style.height = size[1] || size[0]
		}
		// 如果是显示圆形，设置一个很多的半径值即可
		style.borderRadius = props.shape == 'circle' ? '10000px' : props.radius
		return style
	})

	const wrapStyle = computed(() => {
		return {
			...baseStyle.value,
			overflow: ['widthFix','heightFix'].includes(props.mode) ? '' :'hidden'
		}
	})
	const loadingStyle = computed(() => {
		return {
			...baseStyle.value,
			backgroundColor: props.bgColor
		}
	})
	const handleScrollTop = () => {
		// 加载ing时才执行滚动监听判断是否可加载
		if(loading.value || isError.value) return;
		let top = 0
		// #ifndef APP-NVUE
		const res = getRect(ctx,'#'+elId)
		top = res?.top||0
		 // #endif
		if(top - (props.viewHeight||0)<0){
		    loading.value = true;
		}
	}
	
	props.lazyLoad&&watch(() => props.scrollTop, () => {
		handleScrollTop()
	},{
		immediate:true
	})
	


	watch(
		imgSrc,
		() => {
			// #ifndef APP-NVUE
			loading.value = !props.lazyLoad
			 // #endif
			// #ifdef APP-NVUE
			// nvue不支持懒加载
			loading.value = true
			 // #endif
			loadEnd.value = !props.src
			// 如果传入null或者''，或者false，或者undefined，标记为错误状态
			isError.value = !props.src
			props.lazyLoad&&handleScrollTop()
		},
		{
			// 初始化时立即执行一次watch
			immediate: true
		}
	)
	// 阻止事件冒泡
	 const preventEvent = (e: { stopPropagation(): void }) => {
	  e && typeof e.stopPropagation === "function" && e.stopPropagation();
	};

	// 点击图片
	const onClick = (e:{ stopPropagation(): void }) => {
		emit('click')
		props.preview&&uni.previewImage({
			urls:props.previewUrls||[imgSrc.value!],
			current:props.previewCurrent
		})
		props.stop && preventEvent(e)
	}
	// 图片加载失败
	const onErrorHandler = (err : any) => {
		isError.value = true
		emit('error', err)
	}
	// 图片加载完成，标记loading结束
	const onLoadHandler = (event : any) => {
		loading.value = true
		isError.value = false
		loadEnd.value = true
		emit('load', event)
		removeBgColor()
	}
	// 移除图片的背景色
	const removeBgColor = () => {
		// 淡入动画过渡完成后，将背景设置为透明色，否则png图片会看到灰色的背景
		backgroundStyle.value = {
			backgroundColor: 'transparent'
		}
	}
</script>

<style lang="scss" scoped>
	@include appBem_b(components-load-image) {
		position: relative;
		transition: opacity 0.5s ease-in-out;
		&__image{
			top: 0;
			left: 0;
		}
		&__loading,
		&__error {
			position: absolute;
			top: 0;
			left: 0;
			width: 100%;
			height: 100%;
			@include flex;
			align-items: center;
			justify-content: center;
			background-color: #f3f4f6;
			color: #999;
			font-size: 46rpx;
			&__box{
				@include flex;
				align-items: center;
				justify-content: center;
			}
			&__img{
				width: 32rpx;
				height: 32rpx;
			}
		}
		&__loading{
			z-index:10
		}
	}
</style>